package com.huawei.cloudapp.model.direct;

import static com.huawei.cloudapp.utils.CasConstantsUtil.PASSWORD;
import static com.huawei.cloudapp.utils.CasConstantsUtil.TOKEN;
import static com.huawei.cloudapp.utils.CasConstantsUtil.X_SUBJECT_TOKEN;

import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
import static java.net.HttpURLConnection.HTTP_CREATED;
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;

import android.util.Log;

import androidx.annotation.NonNull;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.huawei.cloudapp.model.OnRequestListener;
import com.huawei.cloudapp.model.IUserModel;
import com.huawei.cloudapp.model.bean.CustomException;
import com.huawei.cloudapp.model.bean.User;
import com.huawei.cloudapp.model.bean.direct.UserToken;
import com.huawei.cloudapp.utils.CasHttpUtils;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;

public class UserModel implements IUserModel {

    public static final String TAG = "UserModel";
    private static final String IAM_URL = "https://iam.myhuaweicloud.com/v3/auth/tokens?nocatalog=true";
    private User mUser;
    private String mRegion;

    @Override
    public void getUser(User user, String password, String region, OnRequestListener<User> onRequestListener) {
        Map<String, String> header = new HashMap<>();
        header.put("Content-Type", "application/json;charset=utf8");
        Gson gson = new Gson();
        UserToken userTokenRequest = new UserToken();
        userTokenRequest.getAuth().getIdentity().getMethods().add(PASSWORD);
        userTokenRequest.getAuth().getIdentity().getPassword().getUser().getDomain().setName(user.getUsername());
        userTokenRequest.getAuth().getIdentity().getPassword().getUser().setName(user.getIamUsername());
        userTokenRequest.getAuth().getIdentity().getPassword().getUser().setPassword(password);
        userTokenRequest.getAuth().getScope().getProject().setName(region);
        String userTokenRequestJson = gson.toJson(userTokenRequest);
        CasHttpUtils.post(IAM_URL, header, userTokenRequestJson, new Callback() {
            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                onRequestListener.onFailure(e);
            }
            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) {
                try {
                    onRequestListener.onSuccess(parseResponseToUser(response), 1);
                } catch (Exception e) {
                    onRequestListener.onFailure(e);
                }
            }
        });
        mUser = user;
        mRegion = region;
    }

    @Override
    public List<User> parseResponseToUser(Response response) throws Exception {
        if (response == null) {
            Log.e(TAG, "Failed to get response from server.");
            throw new CustomException.EmptyResponseFromServerException();
        }
        String tokenStr, tokenExpireTimeStr, projectId = null;
        if (response.code() == HTTP_CREATED) {
            tokenStr = response.header(X_SUBJECT_TOKEN);
            if (tokenStr == null || tokenStr.isEmpty()) {
                Log.e(TAG, "Failed to get token.");
                throw new CustomException.FailedToGetTokenException();
            }
            String rspStr = response.body().string();
            if (rspStr.isEmpty()) {
                Log.e(TAG, "Failed to get response from server.");
                throw new CustomException.FailedToGetTokenException();
            }
            Gson gson = new Gson();
            JsonObject jsonObject = gson.fromJson(rspStr, JsonObject.class);
            String tokenExpireTime = jsonObject.get(TOKEN).getAsJsonObject().get("expires_at").getAsString();
            if (tokenExpireTime == null || tokenExpireTime.isEmpty()) {
                Log.e(TAG, "Failed to token expire time.");
                throw new CustomException.FailedToGetTokenException();
            }
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
            format.setTimeZone(TimeZone.getTimeZone("UTC"));
            Date date = format.parse(tokenExpireTime);
            tokenExpireTimeStr = String.valueOf(date.getTime());

            projectId = jsonObject.get(TOKEN).getAsJsonObject().get("project").getAsJsonObject().get("id").getAsString();
            Log.e(TAG, "parseResponseToUser: projectId = " + projectId);
        } else if (response.code() == HTTP_BAD_REQUEST) {
            Log.e(TAG, "The request body is invalid.");
            throw new CustomException.BadRequestException();
        } else if (response.code() == HTTP_UNAUTHORIZED) {
            Log.e(TAG, "The username or password is wrong.");
            throw new CustomException.LogInInfoErrorException();
        } else if (response.code() == HTTP_FORBIDDEN) {
            Log.e(TAG, "No permission to get token.");
            throw new CustomException.ForbiddenException();
        } else if (response.code() == HTTP_NOT_FOUND) {
            Log.e(TAG, "No resource found.");
            throw new CustomException.ResourceNotFoundException();
        } else if (response.code() == HTTP_INTERNAL_ERROR) {
            Log.e(TAG, "Server internal error.");
            throw new CustomException.ServerError();
        } else if (response.code() == HTTP_UNAVAILABLE) {
            Log.e(TAG, "Service Unavailable.");
            throw new CustomException.ServiceUnavailableException();
        } else {
            Log.e(TAG, "Failed to login, code = " + response.code() + ". ");
            throw new CustomException.FailedToLogInException();
        }

        mUser.getUserToken().put(mRegion, tokenStr);
        mUser.getUserTokenExpireTime().put(mRegion, tokenExpireTimeStr);
        mUser.getUserProjectId().put(mRegion, projectId);
        return new ArrayList<>(Collections.singletonList(mUser));
    }

}
